---
title: next-intl 3.0
---

import PartnerContentLink from 'components/PartnerContentLink';

# next-intl 3.0

<small>Nov 14, 2023 · by Jan Amann</small>

More than a year ago, on Oct 25, 2022, [Next.js 13 was announced](https://nextjs.org/blog/next-13) with beta support for the App Router and Server Components. Ever since then, `next-intl` began an exploration on what it means to provide an ideal experience for implementing i18n with the newly added capabilities.

Today, [after more than 370 commits and the involvement of over 60 community members](https://github.com/amannn/next-intl/pull/149), I'm absolutely thrilled to announce **next-intl 3.0**, which is now App Router-first.

If you're still happy with the Pages Router, rest assured that `next-intl` is dedicated to support this paradigm for as long as Next.js does. For those who have already migrated to the App Router, this means that you can take full advantage of the new capabilities with `next-intl`.

## New features

1. **Support for React Server Components**: The APIs `useTranslations`, `useFormatter`, `useLocale`, `useNow` and `useTimeZone` can now be used in Server Components ([docs](/docs/environments/server-client-components)).
2. **New async APIs**: To handle i18n in async components, the Metadata API and Route Handlers, the APIs `getTranslations`, `getFormatter`, `getNow`, and `getTimeZone` have been added ([docs](/docs/environments/actions-metadata-route-handlers)).
3. **Middleware for internationalized routing**: While Next.js has built-in support for this with the Pages Router, the App Router doesn't include a built-in solution anymore. `next-intl` now provides a drop-in solution that has you covered ([docs](/docs/routing/middleware)).
4. **Internationalized navigation APIs**: Similar to the middleware, this provides a drop-in solution that adds internationalization support for Next.js' navigation APIs: `Link`, `useRouter`, `usePathname` and `redirect`. These APIs allow you to handle locale prefixes behind the scenes and also provide support for localizing pathnames (e.g. `/en/about` vs. `/de/ueber-uns`, see the [docs](/docs/routing/navigation)).

The latter two have already been added in minor versions, but 3.0 cleans up the API and includes many improvements and bug fixes.

## Breaking changes

**Users of pre-releases:** If you've already tried out pre-release versions, first of all, thank you so much! Second: Some APIs saw iterations over the pre-release period, please carefully review the breaking changes below, even if you're already using some of the new APIs. Also note the change [from `getTranslator` to `getTranslations`](https://github.com/amannn/next-intl/pull/600) to provide better ergonomics for working with async Server Components.

### Updated setup

`next-intl` now requires two additional setup steps when you're using the App Router:

1.  [The `i18n.ts` module](/docs/getting-started/app-router#i18nts) provides configuration for Server Components
2.  [`next-intl/plugin`](/docs/getting-started/app-router#nextconfigjs) needs to be added to link your `i18n.ts` module to `next-intl`

### New navigation APIs for the App Router

With [v2.14](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md#2140-2023-05-10), the navigation APIs `useRouter`, `usePathname` and `Link` were added to `next-intl` that enabled you to use the APIs you're used to from Next.js while automatically considering a `locale` behind the scenes.

With 3.0, we're cleaning up these APIs by moving them to a shared namespace as well as introducing type-safety for the `locale` prop that can be passed to these APIs.

```diff
- import Link from 'next-intl/link';
- import {useRouter, usePathname} from 'next-intl/client';
- import {redirect} from 'next-intl/server';

+ import {createSharedPathnamesNavigation} from 'next-intl/navigation';
+
+ const locales = ['en', 'de'] as const;
+ const {Link, useRouter, usePathname, redirect} = createSharedPathnamesNavigation({locales});
```

Typically, you'll want to call this factory function in a central place in your app where you can easily import from (see [the navigation docs](/docs/routing/navigation)).

These changes bring the existing APIs in line with the new [`createLocalizedPathnamesNavigation` API](/docs/routing/navigation) that allows you to localize pathnames:

```tsx filename="navigation.ts"
import {createLocalizedPathnamesNavigation, Pathnames} from 'next-intl/navigation';

export const locales = ['en', 'de'] as const;

// The `pathnames` object holds pairs of internal
// and external paths, separated by locale.
export const pathnames = {
  // If all locales use the same pathname, a
  // single external path can be provided.
  '/': '/',
  '/blog': '/blog',

  // If locales use different paths, you can
  // specify each external path per locale.
  '/about': {
    en: '/about',
    de: '/ueber-uns'
  }
} satisfies Pathnames<typeof locales>;

export const {Link, redirect, usePathname, useRouter} =
  createLocalizedPathnamesNavigation({locales, pathnames});
```

By using a similar API, you can upgrade from shared pathnames to localized pathnames by replacing the factory function.

### Switching the middleware default of `localePrefix` to `always`

Previously, the [`localePrefix` of the middleware](/docs/routing#locale-prefix) defaulted to `as-needed`, meaning that a locale prefix was only added for non-default locales.

This default has now been changed to `always` since this has two advantages:

1. We can recommend [a safer default `matcher`](/docs/routing/middleware#matcher-config) that needs no extra treatment for pathnames with dots (e.g. `/users/jane.doe`)
2. It avoids an [edge case of `Link`](/docs/routing#locale-prefix-as-needed) where we include a prefix for the default locale on the server side but patch this on the client side by removing the prefix (certain SEO tools might report a hint that a link points to a redirect in this case).

If you want to stay on the `as-needed` strategy, you can [configure this option](/docs/routing#locale-prefix-as-needed) in the middleware.

### Static rendering of Server Components

With the newly introduced Server Components support comes a temporary workaround for static rendering.

If you call APIs from `next-intl` in Server Components, the page will by default opt into [dynamic rendering](https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering). This is a limitation that we aim to remove in the future, but as a stopgap solution, we've added the [`unstable_setRequestLocale`](/docs/getting-started/app-router/with-i18n-routing#static-rendering) API so that you can keep your pages fully static.

Note that if you're using `next-intl` exclusively in Client Components, this doesn't apply to your app and static rendering will continue to work as it did before.

### Changes to [`NextIntlClientProvider`](/docs/usage/configuration#nextintlclientprovider)

First, the import for this component has changed:

```diff
- import {NextIntlClientProvider} from 'next-intl/client';
+ import {NextIntlClientProvider} from 'next-intl';
```

Depending on if you're using `NextIntlClientProvider` with or without the App Router, there's been another change to consider that was implemented in order to avoid hydration mismatches across the server and client.

**Using `NextIntlClientProvider` without the App Router**

If you're using `NextIntlClientProvider` _without the App Router_ (e.g. with the Pages Router), you need to define the `locale` prop now explicitly (e.g. via `useRouter().locale`). Furthermore, a warning has been added if a `timeZone` hasn't been defined.

**Using `NextIntlClientProvider` with the App Router**

If you're using `NextIntlClientProvider` _with the App Router_, there has been a slight change to the semantics. If you're rendering `NextIntlClientProvider` within a Server Component, the component will pass defaults for `locale`, `now` and `timeZone` to the client side.

If you're already providing these options, then you're set.

If you're not, be aware that this will by default opt the corresponding page into dynamic rendering. If your app relies on static rendering, you can avoid this by passing all of the mentioned options:

```tsx
<NextIntlClientProvider
  messages={messages}
  // By providing these props explicitly,
  // the provider can render statically.
  timeZone="Europe/Vienna"
  now={new Date()}
  locale={locale}
>
  ...
</NextIntlClientProvider>
```

Alternatively, you can explicitly enable static rendering—see [the previous section](#static-rendering-of-server-components).

### Other notable changes

1. `next-intl` now uses [`exports` in `package.json`](https://nodejs.org/api/packages.html#subpath-exports) to clearly define which modules are exported. This should not affect you, unless you've previously imported undocumented internals.
2. `NextIntlProvider` has been removed in favor of [`NextIntlClientProvider`](/docs/usage/configuration#client-server-components)
3. [The middleware](/docs/routing/middleware) now needs to be imported from `next-intl/middleware` instead of `next-intl/server` (deprecated since v2.14).
4. `next@^13.4` is now required for the RSC APIs. Next.js 10–12 is still supported for the Pages Router integration via `use-intl` (see also: [Support for legacy Next.js versions](/docs/getting-started/pages-router#support-for-legacy-nextjs-versions)).
5. `useMessages` now has a non-nullable return type for easier consumption and will throw if no messages are configured.
6. `createTranslator(…).rich` now returns a `ReactNode`. Previously, this was somewhat confusing, since `t.rich` accepted and returned either React elements or strings depending on if you retrieve the fuction via `useTranslations` or `createTranslator`. Now, an explicit [`t.markup`](/docs/usage/messages#html-markup) function has been added to generate markup strings like `'<b>Hello</b>'` outside of React components.
7. `useIntl` has been replaced with [`useFormatter`](/docs/usage/dates-times) (deprecated since v2.11).
8. `createIntl` has been replaced with [`createFormatter`](/docs/environments/core-library) (deprecated since v2.11).
9. `useLocale` now requires `next@>=13.3.0`. If you're on an older version of Next.js and you're using the Pages Router, please use `useRouter().locale` instead.

## Upgrade now

```
npm install next-intl@latest
```

## Thank you

This release was truly a team effort and couldn't be nearly where it is today without the involvement of the community.

Thank you so much for:

- All the encouraging words along the way
- Testing out prereleases
- Providing feedback
- Contributing code
- Questioning ideas
- Helping each other

I had the pleasure to get in touch with so many of you along the way and I'm incredibly grateful for the willingness to help and support each other in our community.

<figure>
<img className="max-w-xl w-full" src="/rsc-pr-participants.png" />

<figcaption>
  Participants who joined the conversation in [pull request
  #149](https://github.com/amannn/next-intl/pull/149).
</figcaption>

</figure>

A special thank you goes to <PartnerContentLink href="https://crowdin.com/">Crowdin</PartnerContentLink>, being the primary sponsor for `next-intl` and enabling me to regularly dedicate time for this project.

—Jan

---

(this post has been updated from an initial announcement for the 3.0 release candidate)
